一、代理简介
代理一词含义十分宽泛,例如金融领域的股票发行代理、营销领域的销售代理、以及计算机领域中的代理设计模式等。尽管代理一词被使用的领域如此广泛,但是代理一词的大致的抽象含义是相似的或者说是相同的。代理是一个被委托人委托其执行如下活动:参加活动、行驶权力、执行任务等。这样理解的话,计算机中某个对象或组件的代理就非常好理解了。
计算机领域中代理的概念是一个十分重要的概念,常见的有代理服务器、代理设计模式等。在软件开发发展成熟的今天,每个工程的代码量也越来越庞大,带来的一个问题就是一次小小的需求修改就会引起很大的变化,从而可能引进新的BUG.
因此程序员对需求修改都深恶痛绝,而代理设计模式在某种程度上可以缓解这种问题。代理设计模式可以实现在不破坏原有代码的情况下,对原有代码添加额外的功能,从而实现以低的侵入完成原有系统功能的扩展,这种设计也符合里氏替换原则(对修改关闭,对扩展开放)。
二、Java语言的代理
编程语言中的代理分为静态代理和动态代理
- 静态代理:在编译时期就已经确定了静态代理的类型或者说是是在编译时期的时候生成代理的类(class)
- 动态代理:在运行时期确定代理的类型或者是说在运行时期生成代理的类(class)
像大多数其他语言一样,Java可以轻松的实现静态代理。具体来讲有两种形式:
静态代理
- 匿名内部类的形式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39abstract class AbstractProxy {
AbstractProxy real;
public AbstractProxy() {
// TODO Auto-generated constructor stub
}
public AbstractProxy(AbstractProxy real) {
this.real = real;
};
//被代理的方法
public abstract void foolbar(String str);
}
//某个被代理类
class RealClass extends AbstractProxy {
public RealClass() {
super();
}
public RealClass(AbstractProxy real) {
super(real);
// TODO Auto-generated constructor stub
}
public void foolbar(String str) {
// TODO Auto-generated method stub
System.out.println("out>>" + str);
}
}
final AbstractProxy realObj = new RealClass();
AbstractProxy proxy = new AbstractProxy() {
public void foolbar(String str) {
// TODO Auto-generated method stub
System.out.println("you are proxied by me!");
realObj.foolbar(str);
}
};
该形式可以实现一个代理,看似是在运行时生成的一个匿名内部类,但是通过测试发现匿名内部类是在编译时期生成的类,这个可以通过匿名内部类类文件来观察,因此其属于静态代理。这种形式的代理看起来不太正常,而且一个代理类只能代理一个接口或者一个抽象类,如果代理多个就必须新增加多个匿名内部类。
- 继承被代理类或者实现被代理接口
这种形式的代理设计类似于设计模式中的装饰器模式或者适配器模式,具体看代码:
1 |
|
这种形式的代理类型需要在编译时期确定,因此属于静态类型。从某种程度上来看,这种形式和装饰器模式、适配器模式的设计思路相似。缺点同第一种形式一样,如果多个需要代理多个接口就需要重写代理类,让其实现多个被代理的接口;同时在类型转换的时候也会很麻烦。
动态代理
java
的动态代理是运行时动态的根据需要被代理的接口列表interfaces生成一个代理类,该代理类实现了接口列表interfaces
中的所有方法,然后在方法的内部实际是讲该方法的调用转发给了实现了InvocationHandler
接口的对象,顾名思义,该对象让包含代理时被代理方法的代理逻辑。
用法举例:编写一个代理类实现拦截某个被代理方法
具体的使用代码:
1 | interface IProxied1 { |
执行代码1
2
3
4
5
6
7
8
9
10
public class DynamicProxyDemo {
public static void main(String[] str) {
IProxied1 proxiedObj = new Proxied();
Object proxyObj = Proxy.newProxyInstance(IProxied1.class.getClassLoader(), new Class<?>[]{IProxied1.class, IProxied2.class}, new Interceptor(proxiedObj));
((IProxied1)proxyObj).proxiedMethod1("Hello, World!");
System.out.println("-------");
((IProxied2)proxyObj).proxiedMethod2("Hello, World!");
}
}
输出如下:
1 |
|
比较静态代理和动态代理
一般来讲,静态代理是硬编码去实现一个代理类,如果需要被代理的接口有变动,则需要重新编码代理类;静态绑定的过程将代理类的代理逻辑和代理类的生成绑定到一起了,
所以修改起来不是很方便(解耦不彻底)。其实我们最关注的是代理类的代理逻辑,因此如果将代理的生成自动化(因为代理类的生成的规则是general
的,可以泛化。先实现被代理的接口、然后方法转发,就是这么简单。),
而将代理逻辑分离出来,所有的代理逻辑全部发生在这里;通过这样的解耦,代码可维护性会增强、侵入性会减小,这就是动态代理的思想。
具体来讲区别如下图:
静态代理
静态代理的代理类多处出现代理逻辑的代码,并且同时静态代理的代理类需要自己硬编码。
动态代理
动态代理的代理类类似于一个方法路由,对被代理对象的任何被代理方法的调用,都会被该路由转发到InvocationHandler代理逻辑处理类中,从而将代理类的生成和代理类的代理逻辑分开。Java
动态代理生成的代理类是直接在内存中按照class
文件格式生成了一个二进制文件,然后类加载器加载该二进制类文件,最后实例化一个代理类的实例。
三、Java动态代理源码分析
前面已经介绍了Java
动态代理的基本用法,主要涉及到如下几个类和方法如下:(JDK7
)
java.lang.reflect.Proxy
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException
sun.misc.ProxyGenerator
public static byte[] generateProxyClass(final String name, Class[] interfaces)
java.lang.reflect.InvocationHandler
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
具体源代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler. This method is equivalent to:
* <pre>
* Proxy.getProxyClass(loader, interfaces).
* getConstructor(new Class[] { InvocationHandler.class }).
* newInstance(new Object[] { handler });
* </pre>
*
* <p>{@code Proxy.newProxyInstance} throws
* {@code IllegalArgumentException} for the same reasons that
* {@code Proxy.getProxyClass} does.
*
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param h the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
* @throws IllegalArgumentException if any of the restrictions on the
* parameters that may be passed to {@code getProxyClass}
* are violated
* @throws NullPointerException if the {@code interfaces} array
* argument or any of its elements are {@code null}, or
* if the invocation handler, {@code h}, is
* {@code null}
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass(loader, interfaces);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
Constructor cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[] { h });
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
throw new InternalError(e.toString());
}
}
1 |
|
1 | /** |
通过反编译生成的动态代理类的文件,可以得到
1 |
|
java
动态代理的应用在框架中十分广泛,例如Spring
框架用动态代理来实现AOP
、Struts2
框架利用动态代理实现拦截器。AOP
中的代理逻辑点
又称为切面的切入点(cut point)
。另外,实现AOP
概念的方式是动态代理,但动态代理的形式有很多种,JDK
提供的这种只是其中一种,还有涉及到类加载器加载类前、加载类后植入字节码等形式。
See Also
[1] 彻底理解JAVA动态代理
[2] JDK动态代理实现原理
[3]AOP动态代理的实现机制